package at.fpmedv.eclipse.helma.views;

import java.util.ArrayList;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;

import at.fpmedv.eclipse.helma.core.CollectionTreeObject;
import at.fpmedv.eclipse.helma.core.CoreFunctionTreeObject;
import at.fpmedv.eclipse.helma.core.FunctionTreeObject;
import at.fpmedv.eclipse.helma.core.HelmaCorePlugin;
import at.fpmedv.eclipse.helma.core.HelmaPrototypeTreeObject;
import at.fpmedv.eclipse.helma.core.HelmaServer;
import at.fpmedv.eclipse.helma.core.ITreeObject;
import at.fpmedv.eclipse.helma.core.ITreeParent;
import at.fpmedv.eclipse.helma.core.MountPointTreeObject;
import at.fpmedv.eclipse.helma.core.PropertyTreeObject;
import at.fpmedv.eclipse.helma.core.SkinTreeObject;

/**
 * This class adds a live view of the running apps to the workbench.
 * <p>
 * It enables you to browse and open the skins and functions.
 */

public class HelmaPrototypeOutlineView extends ViewPart {
	
	private DrillDownAdapter drillDownAdapter;

	/**
	 * this action reloads the tree
	 */
	private Action reloadAction;

	/**
	 * not used by now
	 */
	private Action action2;

	/**
	 * this action opens the coresponding file
	 */
	private Action doubleClickAction;

	/**
	 * holds the core plugin
	 */
	private HelmaServer helmaServer = null;

	/**
	 * The Content provider for the Tree View. Sets up the tree
	 * 
	 * @author fpm
	 * 
	 */
	class ViewContentProvider implements IStructuredContentProvider,
			ITreeContentProvider {
		private ArrayList<ITreeParent> invisibleRoot;

		public void inputChanged(Viewer v, Object oldInput, Object newInput) {
			initialize();
		}

		public void dispose() {
		}

		public Object[] getElements(Object parent) {
			if (parent.equals(getViewSite())) {
				initialize();
				return invisibleRoot.toArray();
			}
			return getChildren(parent);
		}

		public Object getParent(Object child) {
			if (child instanceof ITreeObject) {
				return ((ITreeObject) child).getParent();
			}
			return null;
		}

		public Object[] getChildren(Object parent) {
			if (parent instanceof ITreeParent) {
				return ((ITreeParent) parent).getChildren();
			}
			return new Object[0];
		}

		public boolean hasChildren(Object parent) {
			if (parent instanceof ITreeParent)
				return ((ITreeParent) parent).hasChildren();
			return false;
		}

		/*
		 * We will set up a dummy model to initialize tree hierarchy. In a real
		 * code, you will connect to a real model and expose its hierarchy.
		 */
		public void initialize() {
			// get server
			if (helmaServer == null) {
				helmaServer = HelmaCorePlugin.getDefault().getServer();
			}
			// server started then read apps
			if (helmaServer != null) {
					invisibleRoot = helmaServer.getApplications();
			}
		}
	}

	/**
	 * provides the cool icons from famfam for the different types of tree
	 * objects.
	 * 
	 * @author fpm
	 * 
	 */
	class ViewLabelProvider extends LabelProvider {

		public String getText(Object obj) {
			return obj.toString();
		}

		public Image getImage(Object obj) {
			String imageKey = HelmaPrototypeOutlineActivator.TREE_ICON_APPLICATION;
			if (obj instanceof CoreFunctionTreeObject)
				imageKey = HelmaPrototypeOutlineActivator.TREE_ICON_CORE_CODE;
			else if (obj instanceof FunctionTreeObject)
				imageKey = HelmaPrototypeOutlineActivator.TREE_ICON_CODE;
			else if (obj instanceof SkinTreeObject)
				imageKey = HelmaPrototypeOutlineActivator.TREE_ICON_SKIN;
			else if (obj instanceof HelmaPrototypeTreeObject)
				imageKey = HelmaPrototypeOutlineActivator.TREE_ICON_PROTOTYPE;
			else if (obj instanceof MountPointTreeObject)
				imageKey = HelmaPrototypeOutlineActivator.TREE_ICON_MOUNTPOINT;
			else if (obj instanceof CollectionTreeObject)
				imageKey = HelmaPrototypeOutlineActivator.TREE_ICON_COLLECTION;
			else if (obj instanceof PropertyTreeObject)
				imageKey = HelmaPrototypeOutlineActivator.TREE_ICON_PROPERTY;
			return HelmaPrototypeOutlineActivator.getImage(imageKey);
		}
	}

	/**
	 * Compares and sorts 
	 * apps, prototypes, collections, properties, functions and skins
	 *  
	 * @author fpm
	 * 
	 */
	class HelmaTypesSorter extends ViewerComparator {

		/**
		 * Returns a negative, zero, or positive number depending on whether the
		 * first element is less than, equal to, or greater than the second
		 * element.
		 * <p>
		 * The default implementation of this method is based on comparing the
		 * elements' categories as computed by the <code>category</code>
		 * framework method. Elements within the same category are further
		 * subjected to a case insensitive compare of their label strings,
		 * either as computed by the content viewer's label provider, or their
		 * <code>toString</code> values in other cases. Subclasses may override.
		 * </p>
		 * 
		 * @param viewer
		 *            the viewer
		 * @param e1
		 *            the first element
		 * @param e2
		 *            the second element
		 * @return a negative number if the first element is less than the
		 *         second element; the value <code>0</code> if the first element
		 *         is equal to the second element; and a positive number if the
		 *         first element is greater than the second element
		 */
		public int compare(Viewer viewer, Object e1, Object e2) {
			if (!(e1 instanceof ITreeObject) || !(e2 instanceof ITreeObject))
				return 0;
			ITreeObject tester = (ITreeObject) e1;
			return tester.compare((ITreeObject) e2, getComparator());
		}
	}

	/**
	 * The constructor does nothing
	 */
	public HelmaPrototypeOutlineView() {
	}

	/**
	 * This is a callback that will allow us to create the viewer and initialize
	 * it.
	 */
	public void createPartControl(Composite parent) {
		TreeViewer viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		HelmaPrototypeOutlineActivator.getDefault().setViewer(viewer);
		drillDownAdapter = new DrillDownAdapter(viewer);
		viewer.setContentProvider(new ViewContentProvider());
		viewer.setLabelProvider(new ViewLabelProvider());
		viewer.setComparator(new HelmaTypesSorter());
		viewer.setInput(getViewSite());
		makeActions();
		hookContextMenu();
		hookDoubleClickAction();
		contributeToActionBars();
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				HelmaPrototypeOutlineView.this.fillContextMenu(manager);
			}
		});
		TreeViewer viewer = HelmaPrototypeOutlineActivator.getDefault().getViewer();
		Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, viewer);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(reloadAction);
		manager.add(new Separator());
		manager.add(action2);
	}

	private void fillContextMenu(IMenuManager manager) {
		manager.add(reloadAction);
		manager.add(action2);
		manager.add(new Separator());
		drillDownAdapter.addNavigationActions(manager);
		// Other plug-ins can contribute there actions here
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}

	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(reloadAction);
		manager.add(action2);
		manager.add(new Separator());
		drillDownAdapter.addNavigationActions(manager);
	}

	/**
	 * create the actions for the view
	 */
	private void makeActions() {
		reloadAction = new Action() {
			public void run() {
				if (HelmaPrototypeOutlineActivator.getDefault().getViewer() != null)
					HelmaPrototypeOutlineActivator.getDefault().getViewer().refresh();
			}
		};
		reloadAction.setText("Refresh");
		reloadAction.setToolTipText("Refresh");
		reloadAction.setImageDescriptor(HelmaPrototypeOutlineActivator
				.getImageDescriptor(HelmaPrototypeOutlineActivator.ICON_REFRESH));

		action2 = new Action() {
			public void run() {
				showMessage("Action 2 executed");
			}
		};
		action2.setText("Action 2");
		action2.setToolTipText("Action 2 tooltip");
		action2.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages()
				.getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
		doubleClickAction = new Action() {
			public void run() {
				ISelection selection = HelmaPrototypeOutlineActivator.getDefault().getViewer().getSelection();
				ITreeObject obj = (ITreeObject) ((IStructuredSelection) selection)
						.getFirstElement();
				// check if resource exists
				
				if (obj.getResource() != null && obj.getResource().getResource() != null) {
					HelmaCorePlugin.getDefault().openResource(obj.getResource());
				}
			}
		};
	}

	private void hookDoubleClickAction() {
		HelmaPrototypeOutlineActivator.getDefault().getViewer().addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				doubleClickAction.run();
			}
		});
	}

	private void showMessage(String message) {
		MessageDialog.openInformation(HelmaPrototypeOutlineActivator.getDefault().getViewer().getControl().getShell(),
				"Helma Prototype Outline", message);
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		HelmaPrototypeOutlineActivator.getDefault().getViewer().getControl().setFocus();
	}

}